#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os
import re
from pathlib import Path

from saporacle.comm.common_util import remove_anypath, copy_anypath
from saporacle.comm.constants import SAPOracleErrorCode, SAPOracleFile
from saporacle.comm.logger import logger
from saporacle.comm.saporacle_exception import SAPOracleException


class Repository:

    """挂载远程仓库管理"""

    def __init__(self, data_paths: list[str], cache_paths: list[str]) -> None:
        self.data_paths = data_paths
        self.cache_paths = cache_paths

    """备份管理"""

    def make_data_dirs(self) -> None:
        """
        :raise SAPOracleException:
        """
        non_exists = [p for p in self.data_paths if not os.path.isdir(p)]
        try:
            [os.makedirs(p) for p in non_exists]
        except Exception as e:
            logger.error(f"Exception occurred while making data dirs. {non_exists}", exc_info=1)
            raise SAPOracleException(SAPOracleErrorCode.UNKNOWN_ERROR)
        non_exists and logger.info(f"Made data dirs. {non_exists}")

    def remove_data_dir(self) -> None:
        """
        :raise SAPOracleException:
        """
        [remove_anypath(p) for p in self.data_paths]

    def remove_cache_dir(self) -> None:
        """
        :raise SAPOracleException:
        """
        [remove_anypath(p) for p in self.cache_paths]

    def redirect_data_paths(self, dst: str) -> None:
        """
        清空 dst ，将 self.data_paths 全部子元素软链接到 dst ，更新 self.data_paths
        :raise SAPOracleException:
        """
        try:
            Path(dst).exists() and remove_anypath(dst)
            os.makedirs(dst)
            [not Path(dst, p.name).exists() and Path(dst, p.name).symlink_to(p, target_is_directory=p.is_dir())
             for data_path in self.data_paths for p in Path(data_path).iterdir()]
        except Exception as e:
            logger.error(f"Exception occurred while sym-linking data. {self.data_paths} {dst} {e}", exc_info=1)
            raise SAPOracleException(SAPOracleErrorCode.UNKNOWN_ERROR)
        logger.info(f"Symlinked data. {self.data_paths} -> {dst} {os.listdir(dst)}")
        self.data_paths = [dst]

    def datafiles(self, pattern: str) -> list[str]:
        """
        匹配结果非空
        :raise SAPOracleException: 匹配结果为空
        """
        paths = sum((list(Path(data_path).iterdir()) for data_path in self.data_paths), [])
        files = [str(p) for p in paths if p.is_file() and re.match(pattern, p.name, re.IGNORECASE)]
        if not files:
            logger.error(f"Cannot match file. {pattern} {paths}")
            raise SAPOracleException(SAPOracleErrorCode.BACKUP_COPY_DAMAGED)
        return files

    def download_datafiles(self, pattern: str, dst: str) -> None:
        """
        :raise SAPOracleException:
        """
        files = self.datafiles(pattern)
        [copy_anypath(file, dst) for file in files]

    @staticmethod
    def replace_datafiles(source_oracle_sid: str, oracle_sid: str, file_name: str) -> None:
        """
        :raise SAPOracleException:
        """
        # 读取文件内容
        with open(file_name, 'r') as file:
            sap_profile_content = file.read()
        # 替换字符串
        sap_profile_content = sap_profile_content.replace(f"\\{source_oracle_sid}\\",
                                                       f"\\{oracle_sid}\\")
        sap_profile_content = sap_profile_content.replace(f"'{source_oracle_sid}'",
                                                       f"'{oracle_sid}'")
        sap_profile_content = sap_profile_content.replace(f"cntrl{source_oracle_sid}.dbf",
                                                       f"cntrl{oracle_sid}.dbf")
        sap_profile_content = sap_profile_content.replace(f"\\{source_oracle_sid}arch",
                                                       f"\\{oracle_sid}arch")
        sap_profile_content = sap_profile_content.replace(f"init{source_oracle_sid}",
                                                       f"init{oracle_sid}")
        sap_profile_content = sap_profile_content.replace(f"{source_oracle_sid}",
                                                       f"{oracle_sid}")
        # wq2. --> ec6.
        sap_profile_content = sap_profile_content.replace(f"{source_oracle_sid.lower()}.",
                                                       f"{oracle_sid.lower()}.")
        # 写入替换后的内容
        with open(file_name, 'w') as file:
            file.write(sap_profile_content)

    def brbackup_scn(self) -> int:
        """
        :raise SAPOracleException:
        """
        summary_log = self.datafiles(SAPOracleFile.BRBACKUP_SUMMARY_LOG)[0]
        detail_log_name = Path(self.datafiles(SAPOracleFile.BRBACKUP_DETAIL_LOG)[0]).stem
        lines = Path(summary_log).read_text(encoding="utf-8").split("\n")
        line = next((_l for _l in lines if detail_log_name.replace(".", " ") in _l), "")
        if not line:
            logger.error(f"No line about {detail_log_name} in {summary_log}.")
            raise SAPOracleException(SAPOracleErrorCode.UNKNOWN_ERROR)
        try:
            scn = int(re.split(r"\s+", line)[14])
        except Exception as e:
            logger.error(f"Exception occurred while reading scn. {detail_log_name} {summary_log} {e}", exc_info=1)
            raise SAPOracleException(SAPOracleErrorCode.SUBPROCESS_EXCEPTION)
        logger.info(f"brbackup: {detail_log_name}, scn: {scn}")
        return scn
